package com.seven.annotation;

import com.google.common.util.concurrent.RateLimiter;
import com.seven.commons.AnnotationException;
import com.seven.commons.BaseResultEnum;
import com.seven.thread.SevenRateLimiterEntity;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Aspect
@Component
@Order(1)
public class SevenRateLimitComponent extends BaseComponent {

    private static  final Logger LOGGER = LoggerFactory.getLogger(SevenRateLimitComponent.class);
    /**
     * permitsPerSecond,全局限流的问题，containersLimiter进行单一服务的接口级别的限流容器
     */

    public static final ConcurrentHashMap<String,SevenRateLimiterEntity> containersLimiter = new ConcurrentHashMap<>(32);

    /**
     * 进行创建限流器的锁
     */
    private ReentrantLock lock = new ReentrantLock();


    private final String  annotation= "@within(com.seven.annotation.SevenRateLimit) || @annotation(com.seven.annotation.SevenRateLimit)";

    @Pointcut(annotation)

    public void limitPoint(){}

    @AfterThrowing(pointcut="limitPoint()", throwing= "error")
    public void afterThrowingAdvice(JoinPoint jp, Throwable error){
        System.out.println("Method Signature: "  + jp.getSignature());
        System.out.println("Exception: "+error);
        error.printStackTrace();
    }


    @Around("limitPoint()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature joinPointObject = (MethodSignature) joinPoint.getSignature();
        Method method = joinPointObject.getMethod();
        //方法上面有注解，忽略class上面的值
         boolean flag = method.isAnnotationPresent(SevenRateLimit.class);
        if (flag) {
            final SevenRateLimit annotation = method.getAnnotation(SevenRateLimit.class);
            final String description = annotation.description();
            final long timeout = annotation.timeout();
            final int value = annotation.value();
            String limitAuthor = annotation.limiterAutor();
            if (StringUtils.isEmpty(limitAuthor)){
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    builder.append(joinPoint.getArgs()[i]);
                    limitAuthor = builder.append(joinPoint.getSignature().getName()).toString();
                }
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("method: description {}: seconds {} : value {}:", description, timeout, value);
            }
            SevenRateLimiterEntity limiter = containersLimiter.get(limitAuthor);
            if (Objects.isNull(limiter)) {
                limiter = createLimiter(limitAuthor, value, timeout);
            } else {
                //此时不能修改限流器，直接进行原有的业务逻辑
                LOGGER.info("已经存在限流器，当前限流器参数不起作用...");
                if (limiter.getRateLimiter().tryAcquire(limiter.getTime(),limiter.getTimeUnit())){
                    try {
                        return joinPoint.proceed();
                    } catch (Throwable e) {
                       throwException(e);

                    }
                }else{
                    throw new AnnotationException(BaseResultEnum.UNKNOWN_ERROR.getCode(), BaseResultEnum.UNKNOWN_ERROR.getMsg());
                }
            }
        }
        return  null;
    }

    /**
     * 根据不同的方法进行限流器的创建，防止并发创建,返回包装类型的限流器
     * @param limiterAutor
     * @param value
     */
    public SevenRateLimiterEntity createLimiter(String limiterAutor,int value,long seconds){

        SevenRateLimiterEntity sevenRateLimiterEntity = null;
        try {
            lock.lock();
            RateLimiter limiter = RateLimiter.create(value,seconds,TimeUnit.MILLISECONDS);
            sevenRateLimiterEntity = new SevenRateLimiterEntity();
            sevenRateLimiterEntity.setRateLimiter(limiter);
            sevenRateLimiterEntity.setTime(seconds);
            sevenRateLimiterEntity.setTimeUnit(TimeUnit.MILLISECONDS);
        }catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("创建限流器失败...",e);
            throw new AnnotationException(BaseResultEnum.UNKNOWN_ERROR.getCode(), BaseResultEnum.UNKNOWN_ERROR.getMsg());
        }
        finally {
            //锁的释放
            lock.unlock();
        }
        containersLimiter.put(limiterAutor,sevenRateLimiterEntity);
        return sevenRateLimiterEntity;
    }
}
